﻿/// <reference path="../jquery-1.4.4.js" />
/// <reference path="../jquery-1.4.4-vsdoc.js" />

/*!  
** Copyright (c) 2011 Ray Liang (http://www.dotnetage.com)
** Dual licensed under the MIT and GPL licenses:
** http://www.opensource.org/licenses/mit-license.php
** http://www.gnu.org/licenses/gpl.html
** 
**----------------------------------------------------------------
** title        : DJME TreeView
** version   : 2.0.1
** modified: 2011-2-27
** depends:
**    jquery.ui.core.js
**    jquery.ui.widget.js
**    jquery.ui.mouse.js
**    jquery.ui.draggable.js
**    jquery.ui.droppable.js
**    jquery.tmpl.js
**    editable.js
**----------------------------------------------------------------
*/
(function ($) {
    $.widget("ui.dtree", {
        options: {
            nodeTemplate: "<li><img src='#{url}' class='d-treenode-img' /><span class='d-treenode-text'>#{label}</span></li>",
            enableDropAndDrag: false,
            singlePathExpand: false,
            checkboxes: false,
            showTreeLines: true,
            dragstart: null,
            dropTargets: null,
            drag: null,
            dragstop: null,
            dropped: null,
            dropover: null,
            dropout: null,
            selected: null,
            selectedNode: null,
            checked: null,
            collapsed: null,
            expanded: null,
            nodeClass: null,
            selectedClass: null,
            hoverClass: null,
            disableClass: null,
            beforeNodeLoad: null,
            nodeLoadError: null,
            nodeLoaded: null,
            popupNodes: null,
            duration: 300,
            showEffect: "blind",
            hideEffect: "blind"
        },
        _create: function () {
            this.element.addClass("d-tree");
            this._initNodes($("li:not(.d-menu-item,.d-combo-item)", this.element));
            this._bindingEvents();
        },
        _bindingEvents: function () {
            var eventPrefix = this.widgetEventPrefix;
            if (this.options.selected)
                this.element.bind(eventPrefix + "selected", this.options.selected);

            if (this.options.drag)
                this.element.bind(eventPrefix + "drag", this.options.drag);

            if (this.options.dragstop)
                this.element.bind(eventPrefix + "dragstop", this.options.dragstop);

            if (this.options.dragstart)
                this.element.bind(eventPrefix + "dragstart", this.options.dragstart);

            if (this.options.dropout)
                this.element.bind(eventPrefix + "dropout", this.options.dropout);

            if (this.options.dropover)
                this.element.bind(eventPrefix + "dropover", this.options.dropover);

            if (this.options.dropped)
                this.element.bind(eventPrefix + "dropped", this.options.dropped);

            if (this.options.collapsed)
                this.element.bind(eventPrefix + "collapsed", this.options.collapsed);

            if (this.options.expanded)
                this.element.bind(eventPrefix + "expanded", this.options.expanded);

            if (this.options.checked)
                this.element.bind(eventPrefix + "checked", this.options.checked);

            if (this.options.beforeNodeLoad)
                this.element.bind(eventPrefix + "beforeNodeLoad", this.options.beforeNodeLoad);

            if (this.options.nodeLoadError)
                this.element.bind(eventPrefix + "nodeLoadError", this.options.nodeLoadError);

            if (this.options.nodeLoaded)
                this.element.bind(eventPrefix + "nodeLoaded", this.options.nodeLoaded);

            if (this.options.popupNodes)
                this.element.bind(eventPrefix + "popupNodes", this.options.popupNodes);

            //            if (this.options.selectedKey)
            //                this.selectNodeByKey(this.options.selectedKey);
        },
        _triggerEvent: function (eventName, eventArgs) {
            this.element.trigger(this.widgetEventPrefix + eventName, eventArgs);
        },
        select: function ($li) {
            var opts = this.options,
            $target = $li,
            selectedNodes = $(".d-treenode-selected", this.element);

            if ($li == null)
                $target = this.element.children(".d-treenode").first();

            if ($target.length) {
                if (selectedNodes.length) {
                    selectedNodes.removeClass("d-treenode-selected");
                    if (opts.selectedClass)
                        selectedNodes.removeClass(opts.selectedClass);
                }

                $target.addClass("d-treenode-selected");
                if (opts.selectedClass) $li.addClass(opts.selectedClass);

                opts.selectedNode = $target;
                this._triggerEvent("selected", $target);
            }
        },
        _setOption: function (key, value) {
            if (key == "selectedNode") {
                if (value)
                    this.select(value);
            }
            else {
                this.options[key] = value;
            }
            return this;
        },
        _initNodes: function (_nodes) {
            var self = this, opts = self.options;
            if (_nodes) {
                if (_nodes.length) {
                    _nodes.each(function (i, n) {
                        var $li = $(n);
                        $li.addClass("d-treenode")
                           .wrapInner("<div class='d-treenode-content'></div>");

                        if (opts.nodeClass) $li.addClass(opts.nodeClass);

                        var _child = $li.children(".d-treenode-content").children("ul");
                        if (_child.length) {
                            $li.addClass("d-treenode-parent");
                            if (!_child.hasClass("d-treenodes-holder"))
                                _child.addClass("d-treenodes-holder");
                            _child.appendTo($li);
                        }
                        self._createNodeButton($li);
                        $li.children(".d-treenode-content")
                        //.disableSelection()
                           .click(function (event) {
                               event.stopPropagation();
                               if (!$li.hasClass("d-treenode-disabled"))
                                   self.select($li);
                           })
                           .dblclick(function (event) {
                               self._toggle($li);
                           })
                           .hover(function (event) {
                               if (!$li.hasClass("d-treenode-disabled")) {
                                   $li.addClass("d-treenode-hover");
                                   if (opts.hoverClass) $li.addClass(opts.hoverClass);
                               }
                           },
                            function (event) {
                                if (!$li.hasClass("d-treenode-disabled")) {
                                    $li.removeClass("d-treenode-hover");
                                    if (opts.hoverClass) $li.removeClass(opts.hoverClass);
                                }
                            });

                        if (self.options.checkboxes)
                            self._enableCheckaboxs($li);

                        if (self.options.enableDropAndDrag)
                            self._enableDropAndDrag(n);
                    });
                }
            }
        },
        _enableCheckaboxs: function ($li) {
            var self = this, cbox = $("<input type='checkbox'>").addClass("d-treenode-checkbox").prependTo($li.children(".d-treenode-content"));
            cbox.click(function () {
                var cbs = $li.find(".d-treenode-checkbox");
                if (cbs.length > 0)
                    cbs.attr("checked", cbox.attr("checked"));
                var _parent = $li.parents(".d-treenode");
                if (_parent.length) {
                    var _vals = 0;
                    $li.siblings().each(function (i, n) {
                        if ($(n).children(".d-treenode-content").children(".d-treenode-checkbox").attr("checked") == cbox.attr("checked"))
                            _vals++;
                    });

                    if (_vals == $li.siblings().length)
                        _parent.children(".d-treenode-content").children(".d-treenode-checkbox").attr("checked", cbox.attr("checked"));
                    self._triggerEvent("checked", { node: $li, checked: cbox.attr("checked") });
                }
            });
        },
        _enableDropAndDrag: function (_node) {

            var self = this, opts = this.options;

            $(_node).children(".d-treenode-content").draggable({
                revert: 'invalid',
                greedy: false,
                iframeFix: true,
                helper: 'clone',
                opacity: 0.8,
                start: function (event, ui) {
                    self._triggerEvent("dragstart", $(this).closest(".d-treenode"));
                },
                stop: function (event, ui) {
                    self._triggerEvent("dragstop", $(this).closest(".d-treenode"));
                },
                drag: function (event, ui) {
                    self._triggerEvent("drag", $(this).closest(".d-treenode"));
                }
            });

            if (opts.dropTargets) {
                var _targets = $(opts.dropTargets);
                if (_targets.length) {
                    _targets.droppable({
                        accept: ".d-treenode-content",
                        over: function (event, ui) {
                            self._triggerEvent("dropout", { node: ui.draggable, container: $(this) });
                        },
                        out: function (event, ui) {
                            self._triggerEvent("dropover", { node: ui.draggable, container: $(this) });
                        },
                        drop: function (event, ui) {
                            self._triggerEvent("dropped", { node: ui.draggable, container: $(this) });
                        }
                    });
                }
            }
            //drop to it node
            $(_node).droppable({
                accept: ".d-treenode-content",
                greedy: true,
                out: function (event, ui) {
                    self._triggerEvent("dropout", { node: ui.draggable, targetNode: $(this).closest(".d-treenode") });
                },
                over: function (event, ui) {
                    self._triggerEvent("dropover", { node: ui.draggable, targetNode: $(this).closest(".d-treenode") });
                },
                drop: function (event, ui) {
                    var _srcNode = ui.draggable.closest(".d-treenode"),
                    _srcParent = _srcNode.closest(".d-treenode-parent"),
                     thisNode = $(this).closest(".d-treenode");

                    if (_node == _srcNode[0]) return false; //drop on self

                    if (_srcNode.has(thisNode).length) return false;

                    var _holder = $(">.d-treenodes-holder", this);

                    if (_holder.length == 0) {
                        _holder = $("<ul/>").addClass("d-treenodes-holder");
                        $(this).append(_holder);
                        thisNode.addClass("d-treenode-parent");
                    }
                    _holder.append(_srcNode);

                    if (_srcParent.length) {
                        if (_srcParent.find(".d-treenode").length == 0) {
                            _srcParent.removeClass("d-treenode-parent")
                                       .children(".d-treenodes-holder").remove();
                        }
                    }

                    var _pos = 0;
                    var _children = $(this).find(".d-treeview-node");
                    if (_children.length)
                        _pos = _children.index(_srcNode);
                    self._triggerEvent("dropped", { node: _srcNode, targetNode: thisNode, position: _pos });
                }
            });
        },
        collape: function ($li) {
            if ($li.hasClass("d-treenode-collapsed")) return;

            $li.removeClass("d-treenode-expanded")

            if ($li.hasClass("d-treenode-parent")) {
                $li.find(".d-treenodes-holder:first").hide(this.options.hideEffect, {}, this.options.duration, function () { $li.addClass("d-treenode-collapsed") });
                this._triggerEvent("collapsed", $li);
            }
        },
        expand: function ($li) {
            var self = this;
            if ($li.hasClass("d-treenode-expanded")) return;

            $li.removeClass("d-treenode-collapsed");
            if ($li.hasClass("d-treenode-parent")) {
                $li.find(".d-treenodes-holder:first").show(this.options.showEffect, {}, this.options.duration, function () { $li.addClass("d-treenode-expanded"); });
                if (this.options.singlePathExpand) {
                    var _siblings = $li.siblings().not(".d-treenode-disabled");
                    if (_siblings.length) _siblings.each(function (i, n) {
                        self.collape($(n));
                    });
                }
                this._triggerEvent("expanded", $li);
            }
        },
        _toggle: function ($li) {
            if ($li.hasClass("d-treenode-collapsed")) {
                this.expand($li);
            }
            else {
                if ($li.hasClass("d-treenode-hasChildren")) {
                    this._triggerEvent("popupNodes", $li);
                    //this._trigger("popupNodes", $li);
                    //this._triggerEvent("popupNodes", $li);
                }
                else
                    this.collape($li);
            }
        },
        _createNodeButton: function ($li) {
            var self = this,
            _tbtn = $("<div/>").addClass("d-treenode-button")
                                         .prependTo($li);
            _tbtn.click(function (event) {
                event.stopPropagation();
                if ($li.hasClass("d-treenode-parent") || $li.hasClass("d-treenode-hasChildren"))
                    self._toggle($li);
            });
        },
        loadNodes: function (_url, _httpMethod, _routeData, _parent) {
            var self = this;
            $.ajax({
                type: _httpMethod,
                url: _url,
                dataType: "json",
                data: _routeData,
                beforeSend: function () {
                    // Trigger beforeNodeLoad
                    self._triggerEvent("beforeNodeLoad", _parent);
                },
                error: function (request, textStatus, errorThrown) {
                    //trigger nodeLoadError event
                    self._triggerEvent("nodeLoadError", { node: _parent, exception: { rquest: request, status: textStatus, error: errorThrown} });
                },
                success: function (data) {
                    // trigger nodeLoaded event
                    if ((self.options.nodeTemplate) && (data)) {
                        var $parent = _parent ? $(_parent) : self.element;
                        if ($parent[0].tagName == "LI") {
                            $parent.addClass("d-treenode-parent")
                            //.addClass("d-treenode-collapsed")
                                        .removeClass("d-treenode-hasChildren");
                            $parent = $("<ul/>").addClass("d-treenodes-holder")
                                                            .appendTo(_parent);

                        }
                        $("#" + self.options.nodeTemplate).tmpl(data).appendTo($parent);
                        self._initNodes($("li", $parent));
                        if (_parent)
                            self.expand(_parent);
                        self._triggerEvent("nodeLoaded", _parent);
                    }
                }
            });
        },
        findNodeElement: function (key, value) {
            return this.element.find("li[" + key + "=" + value + "]");
        },
        findNodeElementByKey: function (value) {
            return this.findNodeElement("key", value);
        },
        getCheckedNodes: function () {
            if (this.options.checkboxes) {
                var _checkedboxs = this.element.find(".d-treenode-checkbox");
                if (_checkedboxs.length) {
                    var nodeArgs = new Array();
                    _checkedboxs.each(function (i, n) {
                        if ($(n).attr("checked")) {
                            nodeArgs.push($(n).parent().closest(".d-treenode"));
                        }
                    });
                    return nodeArgs;
                }
            }
            return null;
        },
        addRoot: function (node) {
            this.element.append($(node));
            this._initNodes($(node));
        },
        addNode: function (node) {
            if (this.options.selectedNode) {
                var _holder = $(this.options.selectedNode).children(".d-treenodes-holder");
                if (_holder.length == 0)
                    _holder = $("<ul/>").addClass("d-treenodes-holder").appendTo($(this.options.selectedNode));

                _holder.append($(node));
                this._initNodes($(node));
            } else this.addRoot(node);
        },
        addNodes: function (nodes) {
            if (nodes) {
                if (nodes.length) {
                    var _ns = $(nodes);
                    if (_ns.get(0).tagName == "UL") {
                        _ns = $(nodes).children();
                        this.element.append(_ns);
                        this._initNodes(_ns);
                        this._initNodeHolder(_ns);
                        this.element.find("li").dtree("refresh");
                    }
                }
            }
        },
        destroy: function () {
            $.Widget.prototype.destroy.call(this);
        }
    });
})(jQuery);
